#!/usr/bin/env python3
"""Generate V2 subtitles with correct text + known voiceover segment timing"""

import json, os, re

BASE = r'E:\集群文件夹\factory_os\short_video_real_data_pipeline\phase4b_90s_formal_sample\v22_kimi_claude_loop\full_story_douyin_video'
SUB_DIR = os.path.join(BASE, '04_subtitles', 'v2')
os.makedirs(SUB_DIR, exist_ok=True)

# Correct script text (9 segments, matching the voiceover segments)
SCRIPT_SEGMENTS = [
    "我让 Claude Code 自己做视频。录屏失败、AI 跑偏、从 69 爬到 92。这不是特效，这是真实工作流。",
    "Windows 后台抓不到桌面画面。试了五种录屏方案，全是空文件。录屏路线，彻底失败。",
    "更糟的是，Claude Code 跑去研究亚马逊选品。ChatGPT 看不下去：放弃录屏，让它自己画界面。",
    "于是用 Python 画终端界面、工具调用卡片。四个 AI 代理并行跑，进度条在涨，代码在敲。每条命令都来自真实执行日志。",
    "每轮渲染完 Kimi 审核打分。v1 69 分，改字体 v2 72，加动画 v3 跳到 81。修字幕 v4 85，砍时长 v6 89。V9 冲到 92。",
    "但下一轮有个硬编码没改。画面四个代理，底部只显示三个。92 直接砸到 76。",
    "修了六项回到 88，但回不到 92 了。一个硬编码，一夜回到解放前。最终决定：回滚 V9，冻结。",
    "V9 上线审核门户，HTTP 200 通过。11 轮迭代，1 次崩溃，从 69 到 92。AI 自己审核自己，自己修复自己，自己部署自己。",
    "录屏失败、跑偏、崩过、又爬回来。执行、审核、修复、回归、交付。这才是真正的 AI 工作流。",
]

# Original segment durations before atempo
SEG_DURATIONS = [10.1, 8.6, 9.0, 12.3, 14.8, 8.4, 10.1, 13.3, 10.6]
ATEMPO = 1.05

# Calculate segment timing (proportional to atempo)
total_orig = sum(SEG_DURATIONS)  # 97.2
total_final = total_orig / ATEMPO  # ~92.57

seg_timing = []
current_time = 0.0
for dur in SEG_DURATIONS:
    final_dur = dur / ATEMPO
    seg_timing.append((current_time, current_time + final_dur))
    current_time += final_dur

print("Segment timing (seconds):")
for i, (st, et) in enumerate(seg_timing):
    print(f"  P{i+1}: {st:.2f}s - {et:.2f}s ({et-st:.2f}s)")

# Keywords to highlight with colors
KEYWORD_MAP = {
    "录屏失败": "#FF4444",
    "录屏": "#FF4444",
    "AI 跑偏": "#FF8800",
    "跑偏": "#FF8800",
    "真实执行日志": "#00DDFF",
    "真实日志": "#00DDFF",
    "Kimi": "#00AAFF",
    "69": "#FFD700",
    "72": "#88FF88",
    "81": "#44FF44",
    "85": "#44FFDD",
    "89": "#44DDFF",
    "92 砸到 76": "#FF2222",
    "92": "#FFD700",
    "76": "#FF2222",
    "88": "#FFDD44",
    "HTTP 200": "#44FF44",
    "200": "#44FF44",
    "回滚": "#44FF44",
    "冻结": "#44FFDD",
    "稳定交付": "#44FF44",
    "regression": "#FF2222",
    "自动修复": "#44DDFF",
}

def get_kw_color(text):
    for kw, color in sorted(KEYWORD_MAP.items(), key=lambda x: -len(x[0])):
        if kw in text:
            return color, kw
    return None, None

def format_time(seconds):
    h = int(seconds // 3600)
    m = int((seconds % 3600) // 60)
    s = seconds % 60
    return f"{h:02d}:{m:02d}:{s:06.3f}"

def srt_time(sec):
    return format_time(sec).replace('.', ',')

def ass_time(sec):
    return format_time(sec).replace(',', '.')

# Generate subtitles
all_subs = []
sub_idx = 1

for seg_idx, (text, (seg_start, seg_end)) in enumerate(zip(SCRIPT_SEGMENTS, seg_timing)):
    seg_dur = seg_end - seg_start

    # Split into sentences
    sentences_raw = re.split(r'(?<=[。！？])', text)
    sentences = [s.strip() for s in sentences_raw if s.strip()]

    # Merge short sentences into groups of max 13 chars per line
    line_groups = []
    current_lines = []
    current_chars = 0

    for sent in sentences:
        sent_clean = sent.replace(' ', '')
        sent_chars = len(sent_clean)

        # If this sentence alone would exceed 13 chars, it becomes its own line
        if sent_chars > 13:
            if current_lines:
                line_groups.append(list(current_lines))
                current_lines = []
                current_chars = 0
            current_lines.append(sent)
            current_chars = sent_chars
            line_groups.append(list(current_lines))
            current_lines = []
            current_chars = 0
            continue

        if current_chars + sent_chars <= 13:
            current_lines.append(sent)
            current_chars += sent_chars
            # Max 2 lines per group
            if len(current_lines) >= 2:
                line_groups.append(list(current_lines))
                current_lines = []
                current_chars = 0
        else:
            if current_lines:
                line_groups.append(list(current_lines))
            current_lines = [sent]
            current_chars = sent_chars

    if current_lines:
        line_groups.append(list(current_lines))

    # If no groups (empty), create one
    if not line_groups:
        line_groups.append([text])

    # Distribute timing across groups
    num_groups = len(line_groups)
    group_chars = [sum(len(l.replace(' ', '')) for l in g) for g in line_groups]
    total_chars = sum(group_chars)

    g_start = seg_start
    for gi, (group, gc) in enumerate(zip(line_groups, group_chars)):
        g_dur = (gc / total_chars) * seg_dur
        g_end = g_start + g_dur

        if g_end - g_start < 0.6:
            g_end = g_start + 0.6

        if gi == num_groups - 1:
            g_end = seg_end

        all_subs.append({
            "index": sub_idx,
            "start": round(g_start, 3),
            "end": round(g_end, 3),
            "lines": group
        })
        sub_idx += 1
        g_start = g_end

# --- Generate SRT ---
srt_lines = []
for sub in all_subs:
    srt_lines.append(str(sub['index']))
    srt_lines.append(f"{srt_time(sub['start'])} --> {srt_time(sub['end'])}")
    for line in sub['lines']:
        srt_lines.append(line)
    srt_lines.append("")

srt_path = os.path.join(SUB_DIR, 'captions_final_v2.srt')
with open(srt_path, 'w', encoding='utf-8') as f:
    f.write('\n'.join(srt_lines))
print(f"\nSRT saved: {srt_path} ({len(all_subs)} subtitles)")
print(f"  Total duration: {all_subs[-1]['end']:.1f}s")

# --- Generate ASS with keyword highlighting ---
ass_header = """[Script Info]
ScriptType: v4.00+
PlayResX: 1080
PlayResY: 1920
ScaledBorderAndShadow: yes

[V4+ Styles]
Format: Name, Fontname, Fontsize, PrimaryColour, SecondaryColour, OutlineColour, BackColour, Bold, Italic, Underline, StrikeOut, ScaleX, ScaleY, Spacing, Angle, BorderStyle, Outline, Shadow, Alignment, MarginL, MarginR, MarginV, Encoding
Style: Default,Microsoft YaHei,28,&H00FFFFFF,&H0000FF00,&H00000000,&H80000000,1,0,0,0,100,100,0,0,1,2,0,2,50,50,80,134

[Events]
Format: Layer, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text
"""

def apply_highlighting(text):
    """Apply ASS color tags for keywords"""
    sorted_kws = sorted(KEYWORD_MAP.items(), key=lambda x: -len(x[0]))
    result = []
    remaining = text
    while remaining:
        best_pos = len(remaining)
        best_kw = None
        best_color = None
        for kw, color in sorted_kws:
            pos = remaining.find(kw)
            if pos != -1 and pos < best_pos:
                best_pos = pos
                best_kw = kw
                best_color = color
        if best_kw is not None:
            if best_pos > 0:
                result.append(remaining[:best_pos])
            # Convert #RRGGBB to BBGGRR for ASS
            bgr = best_color[5:7] + best_color[3:5] + best_color[1:3]
            result.append(f"{{\\c&H{bgr}&\\b1}}{best_kw}{{\\c&HFFFFFF&\\b0}}")
            remaining = remaining[best_pos + len(best_kw):]
        else:
            result.append(remaining)
            break
    return "".join(result)

ass_lines = [ass_header]
for sub in all_subs:
    start_str = ass_time(sub['start'])
    end_str = ass_time(sub['end'])
    hl_lines = [apply_highlighting(l) for l in sub['lines']]
    ass_text = "\\N".join(hl_lines)
    ass_lines.append(f"Dialogue: 0,{start_str},{end_str},Default,,0,0,0,,{ass_text}")

ass_path = os.path.join(SUB_DIR, 'captions_final_v2.ass')
with open(ass_path, 'w', encoding='utf-8') as f:
    f.write('\n'.join(ass_lines))
print(f"ASS saved: {ass_path}")

# --- Generate word-level JSON ---
words_json = {
    "total_duration": round(total_final, 1),
    "model": "script+segment-timing",
    "segments": []
}

for seg_idx, (text, (seg_start, seg_end)) in enumerate(zip(SCRIPT_SEGMENTS, seg_timing)):
    clean = text.replace(' ', '')
    cc = len(clean)
    seg_words = []
    if cc > 0:
        char_dur = (seg_end - seg_start) / cc
        for ci, ch in enumerate(clean):
            w_start = seg_start + ci * char_dur
            w_end = seg_start + (ci + 1) * char_dur
            # Check if this character is part of a keyword
            is_hl = False
            for kw in KEYWORD_MAP:
                if kw.startswith(ch) or ch in kw:
                    # Approximate: check if this position has a keyword
                    kw_pos = text.replace(' ', '').find(kw.replace(' ', ''))
                    if kw_pos != -1 and kw_pos <= ci < kw_pos + len(kw.replace(' ', '')):
                        is_hl = True
                        break
            seg_words.append({
                "word": ch,
                "start": round(w_start, 2),
                "end": round(w_end, 2),
                "is_highlight": is_hl
            })
    words_json["segments"].append({
        "start": round(seg_start, 2),
        "end": round(seg_end, 2),
        "text": text,
        "words": seg_words
    })

words_path = os.path.join(SUB_DIR, 'captions_words_v2.json')
with open(words_path, 'w', encoding='utf-8') as f:
    json.dump(words_json, f, ensure_ascii=False, indent=2)
print(f"Word JSON saved: {words_path}")

print(f"\n{'='*50}")
print(f"V2 SUBTITLE GENERATION COMPLETE")
print(f"{'='*50}")
print(f"Total duration: {total_final:.1f}s")
print(f"Subtitle groups: {len(all_subs)}")
print(f"Script segments: {len(SCRIPT_SEGMENTS)}")
for sub in all_subs:
    print(f"  #{sub['index']:2d} [{sub['start']:6.2f}-{sub['end']:6.2f}] {' / '.join(sub['lines'][:2])}")
